package org.bjf.demo.enableclz;

import cn.dev33.satoken.session.SaSession;
import com.alibaba.fastjson2.JSON;
import com.google.common.base.Stopwatch;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.bjf.exception.ServiceException;
import org.bjf.modules.core.web.core.LoginInfo;
import org.bjf.modules.core.web.core.ThreadContext;
import org.bjf.security.satoken.SaTokenAdminUtil;
import org.bjf.security.satoken.SaTokenAppUtil;
import org.bjf.utils.JsonUtil;
import org.springframework.http.MediaType;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

/**
 * @author bjf on 2021/9/27.
 * @version 1.022222
 */
@Slf4j
@WebFilter
public class LogFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        Stopwatch sw = Stopwatch.createStarted();
        String contentType = request.getContentType();
        //对于文件上传，直接放行。
        if (contentType != null && contentType.toLowerCase().contains("multipart")) {
            //直接放行
            chain.doFilter(request, response);
            return;
        }
        RequestBodyWrapper requestWrapper = new RequestBodyWrapper(request);
        String loginInfo = "";
        String uri = request.getRequestURI();
        try {
            SaSession tokenSession;
            // 这里是访问了 redis
            if (StringUtils.contains(uri, "admin/")) {
                tokenSession = SaTokenAdminUtil.getTokenSession();
            } else {
                tokenSession = SaTokenAppUtil.getTokenSession();
            }
            if (tokenSession != null) {
                loginInfo = JSON.toJSONString(tokenSession.get("loginInfo"));
                ThreadContext.setLoginInfo(JsonUtil.parseObject(loginInfo, LoginInfo.class));
            }
        } catch (Exception ignore) {
        }

        // 打印请求参数
//        String requestBody = getRequestBody(request);
        log.info("start uri:{},params:{},loginInfo:{}",
                uri,
                requestWrapper.getBody(),
                loginInfo);
        chain.doFilter(requestWrapper, response);
        long elapsed = sw.elapsed(TimeUnit.MILLISECONDS);
        log.info("end uri:{},status:{},response time:{}", uri, response.getStatus(), elapsed);
        if (elapsed > 1000) {
            log.warn("uri:[{}]响应时间过长:[{} millis]", uri, elapsed);
        }
    }

    private String getRequestBody(HttpServletRequest request) {
        String contentType = request.getContentType();
        if (MediaType.APPLICATION_JSON_VALUE.equals(contentType) || MediaType.APPLICATION_XML.equals(contentType)) {
            try {
                String body = IOUtils.toString(request.getReader());
                return body;
            } catch (IOException e) {
                throw new ServiceException(e.getMessage(), e);
            }
        } else {
            return JSON.toJSONString(request.getParameterMap());
        }
    }

    private class RequestBodyWrapper extends HttpServletRequestWrapper {

        @Getter
        private String body;

        public RequestBodyWrapper(HttpServletRequest request) throws IOException {
            super(request);
            body = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
        }

        @Override
        public ServletInputStream getInputStream() throws IOException {
            final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8));
            return new ServletInputStream() {
                @Override
                public boolean isFinished() {
                    return false;
                }

                @Override
                public boolean isReady() {
                    return false;
                }

                @Override
                public void setReadListener(ReadListener readListener) {

                }

                @Override
                public int read() throws IOException {
                    return bais.read();
                }
            };
        }
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest req) throws ServletException {
        String uri = req.getRequestURI();
        String[] endExclus = {".js", ".gif", ".jpg", ".png", ".svg", ".css", ".ico", ".jsp", ".html"};
        String[] startExclus = {"/druid"};

        return StringUtils.endsWithAny(uri, endExclus) || StringUtils.startsWithAny(uri,startExclus);
    }
}

